#include "libevent_http.h"
#include <event2/event.h>
#include <event2/bufferevent.h>
#include <event2/buffer.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <dirent.h>
#include <ctype.h>
#include "zhttp.h"
#include "utils.h"

void signal_cb(evutil_socket_t fd, short what, void *arg)
{
}
void response_http(struct bufferevent *bev, char *method, char *path, char *param)
{
    //转码将不能时别的中文乱码 -> 中文
    //解码 %23 %34  %5f
    decode_str(path, path);
    //处理path /xxxx
    //去掉path中的/
    char *file = path + 1;
    //如果没有指定访问的资源,默认显示资源目录中的内容
    if (strcmp(path, "/") == 0)
    {
        //file的值, 资源目录的当前的目录
        file = "./";
    }
    //获取文件属性
    struct stat st;
    int ret = stat(file, &st);
    if (ret == -1)
    {
        send_response_head(bev, 404, "File Not Found", ".html", -1);
        send_file(bev, "404.html");
        return;
    }

    //判断是目录还是文件
    if (S_ISDIR(st.st_mode))
    {
        //目录
        //发送头信息
        send_response_head(bev, 200, "OK", get_file_type(".html"), -1);
        //发送目录信息
        send_dir(bev, file);
    }
    else if (S_ISREG(st.st_mode))
    {
        //文件
        //发送消息报头
        send_response_head(bev, 200, "OK", get_file_type(file), st.st_size);
        //发送文件内容
        send_file(bev, file);
    }
}

void conn_readcb(struct bufferevent *bev, void *ctx)
{
    struct zhttp* http = (struct zhttp *)ctx;
    char buf[24] = {0};
    int len = 0;
   while ((len = bufferevent_read(bev, buf, sizeof(buf))) > 0){ 
        append_http_buf(http, buf, len);

        if(http->status == ZHTTP_FLAG_LINE){
            http_line_parse(http, bev);
        }else if(http->status == ZHTTP_FLAG_HEAD){
            http_header_parse(http, bev);
        }else if(http->status == ZHTTP_FLAG_BODY){
            http_body_parse(http, bev); 
        }else if(http->status == ZHTTP_FLAG_DONE){
            break;
        }

        //printf("%d\t%s", len,buf);
        //printf("%s", buf);
        //将buf转为字符串
   }
   
#if 0
    //printf("******** begin call-------- %s\n", __FUNCTION__);
    size_t len = 0; 
    char * buf; 
    struct evbuffer* evb = NULL;
    evb = bufferevent_get_input(bev);
    char path[1024] = {0}; char param[1024] = {0}; char rpath[1024] = {0};
    char method[12],  protocol[12];

    while ((buf = evbuffer_readln(evb, &len, EVBUFFER_EOL_CRLF_STRICT)) != NULL){
        //解析请求行
        if(strlen(path) == 0){
            sscanf(buf, "%[^ ] %[^ ] %[^ ]\r\n", method, path, protocol);
            fprintf(stdout, "method[%s], path[%s], protocol[%s]\n", method, path, protocol);
            sscanf(path, "%[^\?]\?%[^ ]", rpath, param);
            fprintf(stdout, "method[%s], path[%s], protocol[%s], param[%s]\n", method, rpath, protocol,param);
        }else{
            //fprintf(stdout,"%s\n", buf);
            if(strlen(buf) != 0){
                char key[256] ={0};
                char value[1024] ={0};
                sscanf(buf,"%[^:]: %[^\r]\n", key, value);
                printf("%s=%s\n", key, value); 
            }else{
                printf("%s\n",buf);
                break;
            }
        }
    }
    char body[1024] = {0};
    while((len = bufferevent_read(bev, body, sizeof(body))) > 0){
        printf("%s", body);
    }

    if(strncasecmp("get",method, 3) == 0){
        //send_response_head(bev, 200, "OK", get_file_type(".html"), -1);
        response_http(bev, method, rpath, param);
        //bufferevent_free(bev);
    }

    // if(strncasecmp("post",method, 4) == 0){
    //     response_http(bev, method, rpath, param);
    // }
    //printf("******** end call-------- %s\n", __FUNCTION__);
#endif
    return;
}

void conn_eventcb(struct bufferevent *bev, short what, void *ctx)
{
    struct zhttp* http = (struct zhttp *)ctx;
    if (what & BEV_EVENT_TIMEOUT && what & BEV_EVENT_READING){
        printf("客户端连接超时\n");
        bufferevent_free(bev);
        zhttp_clear(http);
    }else if (what & BEV_EVENT_EOF){
        printf("客户端已经断开\n");
        bufferevent_free(bev);
        zhttp_clear(http);
    }else if (what & BEV_EVENT_ERROR){
        printf("some other error\n");
        bufferevent_free(bev);
        zhttp_clear(http);
    }else if (what & BEV_EVENT_CONNECTED){
        printf("客户端主动连接成功\n");
        return;
    }else{
        printf("others\n");
        bufferevent_free(bev);
        zhttp_clear(http);
    }
}
//cfd - 通讯的文件描述符
void listener_cb(struct evconnlistener *listener,
                 evutil_socket_t cfd, struct sockaddr *client, int client_len, void *arg)
{
    printf("******** begin call-------- %s\n", __FUNCTION__);
    printf("cfd is %d\n", cfd);
    struct zhttp* http = (struct zhttp *)arg;
    struct bufferevent *bev = NULL;
    bev = bufferevent_socket_new(http->base, cfd, BEV_OPT_CLOSE_ON_FREE);
    if (!bev)
    {
        fprintf(stderr, "bufferevent_socket_new failed!\n");
        event_base_loopbreak(http->base);
        return;
    }
    bufferevent_flush(bev, EV_READ | EV_WRITE, BEV_NORMAL); //强制把缓冲区的数据刷出去
    bufferevent_setcb(bev, conn_readcb, NULL, conn_eventcb, (void *)http);
    bufferevent_enable(bev, EV_READ | EV_WRITE);
    bufferevent_setwatermark(bev, EV_READ, 0, 30);
    printf("******** end call-------- %s\n", __FUNCTION__);
}
int get_line(struct bufferevent *bev, char *buf, int size)
{
    char c = '\0';
    int i = 0;
    int n;
    while ((i < size - 1) && (c != '\n'))
    {
        n = bufferevent_read(bev, &c, 1);
        if (n <= 0)
        {
            c = '\n';
        }
        else
        {
            if (c == '\r')
            {
                struct evbuffer *evbuf = bufferevent_get_input(bev);
                n = evbuffer_copyout(evbuf, &c, 1);
                if ((n > 0) && (c == '\n'))
                {
                    bufferevent_read(bev, &c, 1);
                }
                else
                {
                    c = '\n';
                }
            }
            buf[i] = c;
            ++i;
        }
    }

    buf[i] = '\0';

    if (n <= 0)
    {
        i == -1;
    }

    return i;
}

//发送响应头
void send_response_head(struct bufferevent *bev, int no, const char *desp, const char *type, long len)
{
    char buf[1024] = {0};
    //状态行
    sprintf(buf, "http/1.1 %d %s\r\n", no, desp);
    bufferevent_write(bev, buf, strlen(buf));
    //消息报头
    sprintf(buf, "Content-Type:%s\r\n", type);
    sprintf(buf + strlen(buf), "Content-Length:%ld\r\n", len);
    bufferevent_write(bev, buf, strlen(buf));
    //空行
    bufferevent_write(bev, "\r\n", 2);
}

//编码,用作回写浏览器的时候,将除字母数字及/_.-~以外的字符转义后回写
void decode_str(char *to, char *from)
{
    for (; *from != '\0'; ++to, ++from)
    {
        if (from[0] == '%' && isxdigit(from[1]) && isxdigit(from[2]))
        {
            //依次判断from中 %20 三个字符
            *to = hexit(from[1]) * 16 + hexit(from[2]);
            //移动已经处理的两个字符(%21指针指向1),表达式3的++from还会再向后移一个字符
            from += 2;
        }
        else
        {
            *to = *from;
        }
    }
    *to = '\0';
}

//发送文件
void send_file(struct bufferevent *bev, const char *filename)
{
    //打开文件
    int fd = open(filename, O_RDONLY);
    if (fd == -1){
        //show 404
        return;
    }
    // 循环的读文件
    char buf[4096] = {0};
    int len = 0;
    while ((len = read(fd, buf, sizeof(buf))) > 0)
    {
        //发送读出的数据
        bufferevent_write(bev, buf, len);
    }
    if (len == -1)
    {
        perror("read file  error");
        exit(1);
    }
    
}

void send_dir(struct bufferevent *bev, const char *dirname)
{
    //拼一个html页面<table></table>
    char buf[4096] = {0};
    sprintf(buf, "<html><head><title>目录名:%s</title></head>", dirname);
    sprintf(buf + strlen(buf), "<body><h1>当前目录:%s</h1><table>", dirname);
    //目录项二级指针
    struct dirent **ptr;
    int num = scandir(dirname, &ptr, NULL, alphasort);
    char path[1024] = {0};
    char enstr[1024] = {0};
    char utime[80] = {0};
    struct stat st;
    for (int i = 0; i < num; i++)
    {
        char *name = ptr[i]->d_name;
        //拼接文件的完整路径
        sprintf(path, "%s/%s", dirname, name);
        printf("path = %s =============\n", path);
        memset(&st, 0, sizeof(struct stat));
        stat(path, &st);
        encode_str(enstr, sizeof(enstr), name);                                                //URL编码
        strftime(utime, sizeof(utime), "%Y-%m-%d %H:%M:%S", localtime((time_t *)&st.st_mtim)); //格式化时间
        //如果是文件
        if (S_ISREG(st.st_mode))
        {
            sprintf(buf + strlen(buf), "<tr><td><a href=\"%s\">%s</a><td><td>%ld</td><td>%s<td></tr>",
                    enstr, name, st.st_size, utime);
        }
        else if (S_ISDIR(st.st_mode))
        { //如果是目录
            sprintf(buf + strlen(buf), "<tr><td><a href=\"%s\">%s</a><td><td>%ld</td><td>%s</td></tr>",
                    enstr, name, st.st_size, utime);
        }
        bufferevent_write(bev, buf, strlen(buf));
        memset(buf, 0, sizeof(buf));
    }
    sprintf(buf, "<table></body></html>");
    bufferevent_write(bev, buf, strlen(buf));
    printf("dir message send ok!!\n");
}

/**
 * 这里的内容是处理%20之类的东西!是解码过程
 * %20 URL编码中的' '(space)
 * %21 '!' %22 '"' %23 '#' %24 '$'
 * %25 '%' %26 '&' %27 '''  %28 '('
 * 相关知识html中的' '(space)是&nbsp
*/
void encode_str(char *to, int tosize, const char *from)
{
    int tolen;
    for (tolen = 0; *from != '\0' && tolen + 4 < tosize; ++from)
    {
        if (isalnum(*from) || strchr("/_.-~", *from) != (char *)0)
        {
            *to = *from;
            ++to;
            ++tolen;
        }
        else
        {
            // %23 %04
            sprintf(to, "%%%02x", (int)*from & 0xff);
            to += 3;
            tolen += 3;
        }
    }
    *to = '\0';
}
//16进制转化为10进制
int hexit(char c)
{
    if (c >= '0' && c <= '9')
    {
        return c - '0';
    }
    if (c >= 'a' && c <= 'f')
    {
        return c - 'a' + 10;
    }
    if (c >= 'A' && c <= 'F')
    {
        return c - 'A' + 10;
    }
    return 0;
}

//通过文件名获取文件类型
const char *get_file_type(const char *name)
{
    char *dot;
    //自右向左查找'.'字符,如不存在返回NULL
    dot = strrchr(name, '.');
    if (dot == NULL)
    {
        return "text/plain; charset=utf-8";
    }
    if (strcmp(dot, ".html") == 0 || strcmp(dot, ".htm") == 0)
    {
        return "text/html; charset=utf-8";
    }
    if (strcmp(dot, ".jpg") == 0 || strcmp(dot, ".jpeg") == 0)
    {
        return "image/jpeg";
    }
    if (strcmp(dot, ".gif") == 0)
    {
        return "image/gif";
    }
    if (strcmp(dot, ".png") == 0)
    {
        return "image/png";
    }
    if (strcmp(dot, ".css") == 0)
    {
        return "text/css";
    }
    if (strcmp(dot, ".au") == 0)
    {
        return "audio/basic";
    }
    if (strcmp(dot, ".wav") == 0)
    {
        return "audio/wav";
    }
    if (strcmp(dot, ".avi") == 0)
    {
        return "video/x-msvideo";
    }
    if (strcmp(dot, ".mov") == 0 || strcmp(dot, ".qt") == 0)
    {
        return "video/quicktime";
    }
    if (strcmp(dot, ".mpeg") == 0 || strcmp(dot, ".mpe") == 0)
    {
        return "video/mpeg";
    }
    if (strcmp(dot, ".vrml") == 0 || strcmp(dot, ".wrl") == 0)
    {
        return "model/vrml";
    }
    if (strcmp(dot, ".midi") == 0 || strcmp(dot, ".mid") == 0)
    {
        return "audio/midi";
    }
    if (strcmp(dot, ".mp3") == 0)
    {
        return "audio/mpeg";
    }
    if (strcmp(dot, ".ogg") == 0)
    {
        return "application/ogg";
    }
    if (strcmp(dot, ".pac") == 0)
    {
        return "application/x-ns-proxy-autoconfig";
    }
    return "text/plain; charset=utf-8";
}